﻿/*
 * DotNetCrypt - an open source library of cryptographic algorithms for .NET
 * Copyright (C) 2009 David Musgrove
 * 
 * This file is part of DotNetCrypt.
 *
 * DotNetCrypt is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DotNetCrypt is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace DotNetCrypt
{
    /// <summary>
    /// Represents a cryptographic object identifier, and exposes methods for
    /// instantiating configured instances of the associated cryptographic
    /// primitives.
    /// </summary>
    /// <remarks>
    /// Unlike the Microsoft <see cref="Oid" /> class, which is implemented as
    /// a sealed class that is closely tied to the CryptoAPI, this class is
    /// intended to be extensible. Thus implementers of non-included algorithms
    /// can extend this class to include these.
    /// </remarks>
    public class Oid
    {
        static private readonly IDictionary<string, Oid> _recognisedAlgorithms = new Dictionary<string, Oid>();

        /// <summary>
        /// Exposes the internal dictionarof recognised algorithms so that a
        /// library that extends DotNetCrypt can add to the functionality of
        /// this class.
        /// </summary>
        static protected IDictionary<string, Oid> RecognisedAlgorithms
        {
            get { return _recognisedAlgorithms; }
        }

        private readonly Type _type;
        private readonly string _friendlyName;
        private readonly string _oid;
        private readonly IHasOid _instance;

        static Oid()
        {
            foreach (var type in typeof(Oid).Assembly.GetTypes())
            {
                if (type.IsInterface || type.IsAbstract) continue;
                if (!typeof(IHasOid).IsAssignableFrom(type)) continue;
                var instance = (IHasOid)Activator.CreateInstance(type);
                foreach (var oid in instance.Oids)
                {
                    _recognisedAlgorithms.Add(oid, new Oid(oid, instance));
                }
            }
        }

        /// <summary>
        /// Creates a new <see cref="Oid" /> instance, with the given OID value
        /// and using the provided <see cref="IHasOid" /> instance to expose
        /// property values and methods.
        /// </summary>
        /// <param name="oid">
        /// The OID valu.
        /// </param>
        /// <param name="instance">
        /// An instance of the primitive implementing the algorithm identified
        /// by the OID.
        /// </param>
        protected Oid(string oid, IHasOid instance)
        {
            _type = instance.GetType();
            _oid = oid;
            _friendlyName = instance.FriendlyName;
            _instance = instance;
        }

        /// <summary>
        /// Gets the OID value.
        /// </summary>
        public string Value
        {
            get { return _oid; }
        }

        /// <summary>
        /// Gets the friendly name of the cryptographic primitive.
        /// </summary>
        public string FriendlyName
        {
            get { return _friendlyName; }
        }

        /// <summary>
        /// Gets the default implementing <see cref="Type"/> for the
        /// cryptographic primitive.
        /// </summary>
        public Type DefaultType
        {
            get { return _type; }
        }

        /// <summary>
        /// Gets configured <see cref="SymmetricAlgorithm" /> instance for the
        /// algorithm identified by the OID.
        /// </summary>
        /// <param name="oid">
        /// The identifying OID.
        /// </param>
        /// <returns>
        /// A configured <see cref="SymmetricAlgorithm" /> instance.
        /// </returns>
        static public SymmetricAlgorithm GetSymmetricAlgorithm(string oid)
        {
            Oid fromOid = FromOid(oid);
            if (fromOid == null) return null;
            return fromOid.GetSymmetricAlgorithm();
        }

        /// <summary>
        /// Gets configured <see cref="SymmetricAlgorithm" /> instance for the
        /// algorithm identified by the OID.
        /// </summary>
        /// <returns>
        /// A configured <see cref="SymmetricAlgorithm" /> instance.
        /// </returns>
        public SymmetricAlgorithm GetSymmetricAlgorithm()
        {
            return _instance.CreateFromOid(Value) as SymmetricAlgorithm;
        }

        /// <summary>
        /// Returns the OID instance for the given friendly name, or
        /// <b>null</b> (<b>Nothing</b> in Visual Basic) if the OID is not
        /// recognised.
        /// </summary>
        /// <param name="name">
        /// The friendly name for the algorithm.
        /// </param>
        /// <returns>
        /// An <see cref="Oid"/> instance, or <b>null</b> (<b>Nothing</b> in
        /// Visual Basic).
        /// </returns>
        static public Oid FromName(string name)
        {
            foreach (var oid in _recognisedAlgorithms.Values)
            {
                if (oid.FriendlyName == name) return oid;
            }
            return null;
        }

        /// <summary>
        /// Returns the OID instance for the given OID, or <b>null</b>
        /// (<b>Nothing</b> in Visual Basic) if the OID is not recognised.
        /// </summary>
        /// <param name="oid">
        /// The OID for the algorithm.
        /// </param>
        /// <returns>
        /// An <see cref="Oid"/> instance, or <b>null</b> (<b>Nothing</b> in
        /// Visual Basic).
        /// </returns>
        static public Oid FromOid(string oid)
        {
            if (!_recognisedAlgorithms.ContainsKey(oid)) return null;
            return _recognisedAlgorithms[oid];
        }
    }
}
